package com.cw.base.jeeyc.controller;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.service.GenericService;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.Strings;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.cw.base.jeeyc.biz.intf.ApiDatabaseBiz;
import com.cw.base.jeeyc.biz.intf.IExceptionCallbackBiz;
import com.cw.base.jeeyc.api.intf.HelloApi;
import com.cw.elasticsearch.dto.ElasticDto;
import com.cw.elasticsearch.service.ElasticsearchService;
import com.cw.lang.common.framework.handle.DubboGenericServiceHandle;
import com.cw.lang.common.utils.DateUtil;
import com.cw.lang.common.utils.GsonUtil;
import com.ruoyi.yz.facade.dto.request.YzVideoReqDto;

import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("")
@Slf4j
public class HelloController {
    @Autowired
    private ApiDatabaseBiz apiDatabaseBiz;
    @DubboReference(group = "hrkj-jeeyc-center", version = "1.0.0", protocol = "dubbo", check = false, timeout = 60000)
    private HelloApi helloApi;
    @Autowired
    private IExceptionCallbackBiz exceptionCallbackBiz;
    @Autowired
    private DubboGenericServiceHandle dubboGenericServiceHandle;
    @Autowired
    private ElasticsearchService elasticsearchService;
    @Resource
    private RedissonClient redissonClient;
    int i = 0;

    @RequestMapping("/order/message2")
    public String message2() {
        i++;
        //异常比例为0.333
        if (i % 3 == 0) {
            throw new RuntimeException();
        }
        return "message2";
    }

    @RequestMapping("/hello")
    @SentinelResource("hello")
    public void hello(String userName, String userAge) {
        //log.info(demoApi.sayHello("chenw"));
        log.info(helloApi.hello("hhh"));
        apiDatabaseBiz.hello();
    }

    @RequestMapping("/jiangji")
    public void jiangji() throws InterruptedException {
        Thread.sleep(5000);
        log.info(helloApi.hello("jiangji"));
    }

    @RequestMapping("elastic")
    public void testElasticSearch() {
        //创建索引
        //boolean create = elasticsearchService.createIndexTwo("book");
        boolean exists = elasticsearchService.existsIndex("book");
        boolean insertOk = elasticsearchService.insertDocument("book", "2", GsonUtil.objectToJsonString(
            ElasticDto.builder()
                .createTime(DateUtil.date2String(new Date(), "yyyy-MM-dd"))
                .name("java编程思想")
                .number("100")
                .price(new BigDecimal(1200))
                .title("java编程思想")
                .build()));
        boolean existDocument = elasticsearchService.existsDocument("book", "2");
        String[] includes = new String[] {"name", "createTime", "title"};
        String[] excludes = Strings.EMPTY_ARRAY;
        GetResponse getResponse = elasticsearchService.getDocumentById("book", "2", includes, excludes);
        SearchResponse searchResponse = elasticsearchService.searchDocument("book", "title", "java");
    }

    @RequestMapping("/testDubboService")
    public void testDubboService() {
        GenericService query = dubboGenericServiceHandle.getGenericService("hrkj-ruoyi-center",
            "com.ruoyi.yz.facade.intf.IYzVideoQuery", "1.0.0");
        YzVideoReqDto reqDto = new YzVideoReqDto();
        reqDto.setId(1111L);
        Map paramMap = JSON.parseObject(GsonUtil.objectToJsonString(reqDto));
        Object object = query.$invoke("selectPage", new String[] {"com.ruoyi.yz.facade.dto.request.YzVideoReqDto"},
            new Object[] {paramMap});
        log.info(GsonUtil.objectToJsonString(object));
    }

    @RequestMapping("/testCallback")
    public void testCallback() {
        exceptionCallbackBiz.testCallback();

    }

    @RequestMapping("/testSpringRetry")
    public void testSpringRetry() {
        vpmsRetryCoupon("43333");
    }

    public Boolean vpmsRetryCoupon(final String userId) {
        // 构建重试模板实例
        RetryTemplate retryTemplate = new RetryTemplate();
        // 设置重试策略，主要设置重试次数
        SimpleRetryPolicy policy = new SimpleRetryPolicy(10, Collections.singletonMap(Exception.class, true));
        // 设置重试回退操作策略，主要设置重试间隔时间
        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(100);
        retryTemplate.setRetryPolicy(policy);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
        // 通过RetryCallback 重试回调实例包装正常逻辑逻辑，第一次执行和重试执行执行的都是这段逻辑
        //RetryContext 重试操作上下文约定，统一spring-try包装
        final RetryCallback<Object, Exception> retryCallback = context -> {
            boolean result = pushCouponByVpmsaa(userId);
            if (!result) {
                throw new RuntimeException();//这个点特别注意，重试的根源通过Exception返回
            }
            return true;
        };
        // 通过RecoveryCallback 重试流程正常结束或者达到重试上限后的退出恢复操作实例
        final RecoveryCallback<Object> recoveryCallback = context -> {
            log.info("正在重试发券::::::::::::" + userId);
            return null;
        };
        try {
            // 由retryTemplate 执行execute方法开始逻辑执行
            retryTemplate.execute(retryCallback, recoveryCallback);
        } catch (Exception e) {
            log.info("发券错误异常========" + e.getMessage());
            e.printStackTrace();
        }
        return true;
    }

    public Boolean pushCouponByVpmsaa(String userId) {
        Random random = new Random();
        int a = random.nextInt(10);
        System.out.println("a是" + userId + a);
        if (a == 8) {
            return true;
        } else {
            return false;
        }
    }

    @RequestMapping("/testLock")
    public void testLock() {
        RLock rLock = redissonClient.getLock("lockKey1");
        try {
            boolean res = rLock.tryLock(10, TimeUnit.SECONDS);
            if (res) {
                log.info("加锁成功");
                log.info("开始做业务");
            } else {
                log.info("加锁失败，退出");
            }
            log.info("锁是否有效：{}", rLock.isLocked());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
            log.info("锁释放后锁是否有效：{}", rLock.isLocked());
        }
    }

    int j = 0;
    @SentinelResource(value = "message", blockHandler = "blockHandler",
        //指定发生BlockException时进入的方法
        fallback = "fallback"
        //指定发生Throwable时进入的方法
    )
    @RequestMapping("/message")
    public String message() {
        j++;
        //方式二: 将限流和降级方法外置到单独的类中
        if (j % 3 == 0) {
            throw new RuntimeException();
        }
        return "message";
    }

    //BlockException时进入的方法
    public String blockHandler(BlockException ex) {
        log.error("{}", ex);
        return "接口被限流或者降级了...";
    }

    //Throwable时进入的方法
    public String fallback(Throwable throwable) {
        log.error("{}", throwable);
        return "接口发生异常了...";
    }

    @SentinelResource(value = "message", blockHandler = "blockHandler", blockHandlerClass = BlockHandlerClass.class,
        //指定发生BlockException时进入的方法
        fallback = "fallback", fallbackClass = FallbackHandlerClass.class
        //指定发生Throwable时进入的方法
    )
    @RequestMapping("/message1")
    public String message1() {
        j++;
        //方式二: 将限流和降级方法外置到单独的类中
        if (j % 3 == 0) {
            throw new RuntimeException();
        }
        return "message";
    }

    class BlockHandlerClass {
        public String blockHandler(BlockException ex) {
            log.error("{}", ex);
            return "接口被限流或者降级了...";
        }
    }

    class FallbackHandlerClass {
        public String fallback(Throwable throwable) {
            log.error("{}", throwable);
            return "接口发生异常了...";
        }
    }
}
